package ffmpeg

import (
	"context"
	"errors"
	"golang.org/x/sync/semaphore"
	"log"
	"os/exec"
	"sync"
)

type (
	// ConvertArgs 转换视频命令参数
	ConvertArgs struct {
		Src string // 原文件地址
		Dst string // 输出文件地址
		*CodecArg
	}

	// CodecArg 音视频编码
	CodecArg struct {
		VideoCodec string   // 视频编码
		AudioCodec string   // 音频编码
		Args       []string // 其他命令参数
	}

	// ConvertVideo 转换视频
	ConvertVideo struct {
		Src    string // 被转换原文件路径
		OutExt string // 输出文件扩展
		*CodecArg
	}

	// ChanResult 转换视频结果
	ChanResult struct {
		OutFile string
		Err     error
	}
)

// BatchConvertVideos 批量转换视频
func (f *SFFmpeg) BatchConvertVideos(ctx context.Context, maxWorkers int, videos ...*ConvertVideo) (outFiles []string, err error) {
	// 检测转换视频参数
	if len(videos) == 0 || (len(videos) > 0 && videos[0] == nil) {
		return nil, errors.New(`缺少需要转换的视频内容`)
	}
	var (
		wg      = sync.WaitGroup{}
		results = make(chan ChanResult, len(videos))
		sem     = semaphore.NewWeighted(int64(maxWorkers))
	)
	// 开启协程迭代生成
	for _, video := range videos {
		// 并发控制
		if err = sem.Acquire(ctx, 1); err != nil {
			close(results) // 提前关闭通道，避免goroutine泄漏
			return nil, err
		}
		wg.Add(1)
		go func(video *ConvertVideo) {
			defer func() {
				wg.Done()      // 结束任务
				sem.Release(1) // 发布一个任务
			}()
			// 检查context是否已被取消
			select {
			case <-ctx.Done():
				return
			default:
				// 执行转换
				var result ChanResult
				result.OutFile, result.Err = f.ConvertVideo(video.Src, video.OutExt, video.CodecArg)
				// 执行入队
				results <- result
			}
		}(video)
	}
	// 等待协程执行完成
	wg.Wait()
	close(results)

	// 处理结果集
	return f.processResults(results)
}

// ConvertVideo 转换视频格式
func (f *SFFmpeg) ConvertVideo(src, outExt string, codecArgs ...*CodecArg) (outFile string, err error) {
	defer func() {
		if err != nil {
			log.Printf(`视频转换失败：%v`, err)
		}
	}()
	// 设置输出文件
	outFile, err = f.outFile(src, outExt, `conv`)
	if err != nil {
		return ``, err
	}

	// 音视频编码
	var codecArg *CodecArg
	if len(codecArgs) > 0 && (len(codecArgs) > 0 && codecArgs[0] != nil) {
		codecArg = codecArgs[0]
	}

	// 命令参数
	cmdArg := ConvertArgs{
		Src:      src,
		Dst:      outFile,
		CodecArg: codecArg,
	}

	// 设定执行命令参数
	cmd := exec.Command(f.Driver, f.ConvertCommandArgs(outExt, cmdArg)...)
	// 执行命令
	if err = f.executeCommand(cmd); err != nil {
		return ``, err
	}
	return outFile, nil
}

// ConvertCommandArgs 转换视频命令大全
func (f *SFFmpeg) ConvertCommandArgs(ext string, otherArg ConvertArgs) []string {
	baseArgs := []string{"-i", otherArg.Src}
	if otherArg.CodecArg != nil && len(otherArg.CodecArg.Args) > 0 {
		// 假设 CodecArg 有一个方法或字段来返回其参数
		baseArgs = append(baseArgs, otherArg.CodecArg.Args...)
	}
	// 获取其他命令
	if args, ok := f.ArgsMapping()[ext]; ok {
		baseArgs = append(baseArgs, args...)
	} else if otherArg.VideoCodec != `` && otherArg.AudioCodec != `` {
		// 默认其他参数
		baseArgs = append(baseArgs, []string{"-c:v", otherArg.VideoCodec, "-c:a", otherArg.AudioCodec}...)
	}
	// 补充转换文件
	return append(baseArgs, otherArg.Dst)
}
